#include "stdafx.h"
#include "LuaCommand.h"
#include "SourceManager.h"
#include "Breakpoint.h"
#include "LogData.h"

#include "../cppx.core/fuVectorStream.hpp"
using cppx::vector_istream;
using cppx::vector_ostream;

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/shared_ptr.hpp>

namespace LuaDebug {

    typedef boost::archive::xml_oarchive serialize_oarchive;
    typedef boost::archive::xml_iarchive serialize_iarchive;
    //typedef boost::archive::text_oarchive serialize_oarchive;
    //typedef boost::archive::text_iarchive serialize_iarchive;

    /**
    * @brief Serializer class
    */
    struct Serializer {
        template<class T0>
        static data_container_t ToData(const T0 &value0) {
            vector_ostream stream;
            serialize_oarchive ar(stream);

            ar << BOOST_SERIALIZATION_NVP(value0);
            stream.flush();
            return stream.container();
        }

        template<class T0, class T1>
        static data_container_t ToData(const T0 &value0, const T1 &value1) {
            vector_ostream stream;
            serialize_oarchive ar(stream);

            ar << BOOST_SERIALIZATION_NVP(value0);
            ar << BOOST_SERIALIZATION_NVP(value1);
            stream.flush();
            return stream.container();
        }

        template<class T0, class T1, class T2>
        static data_container_t ToData(const T0 &value0, const T1 &value1, const T2 &value2) {
            vector_ostream stream;
            serialize_oarchive ar(stream);

            ar << BOOST_SERIALIZATION_NVP(value0);
            ar << BOOST_SERIALIZATION_NVP(value1);
            ar << BOOST_SERIALIZATION_NVP(value2);
            stream.flush();
            return stream.container();
        }

        template<class T0, class T1, class T2, class T3>
        static data_container_t ToData(const T0 &value0, const T1 &value1, const T2 &value2, const T3 &value3) {
            vector_ostream stream;
            serialize_oarchive ar(stream);

            ar << BOOST_SERIALIZATION_NVP(value0);
            ar << BOOST_SERIALIZATION_NVP(value1);
            ar << BOOST_SERIALIZATION_NVP(value2);
            ar << BOOST_SERIALIZATION_NVP(value3);
            stream.flush();
            return stream.container();
        }

        template<class T0>
        static void ToValue(const data_container_t &data, T0 &value0) {
            vector_istream stream(data);
            serialize_iarchive ar(stream);

            ar >> BOOST_SERIALIZATION_NVP(value0);
        }

        template<class T0, class T1>
        static void ToValue(const data_container_t &data, T0 &value0, T1 &value1) {
            vector_istream stream(data);
            serialize_iarchive ar(stream);

            ar >> BOOST_SERIALIZATION_NVP(value0);
            ar >> BOOST_SERIALIZATION_NVP(value1);
        }

        template<class T0, class T1, class T2>
        static void ToValue(const data_container_t &data, T0 &value0, T1 &value1, T2 &value2) {
            vector_istream stream(data);
            serialize_iarchive ar(stream);

            ar >> BOOST_SERIALIZATION_NVP(value0);
            ar >> BOOST_SERIALIZATION_NVP(value1);
            ar >> BOOST_SERIALIZATION_NVP(value2);
        }

        template<class T0, class T1, class T2, class T3>
        static void ToValue(const data_container_t &data, T0 &value0, T1 &value1, T2 &value2, T3 &value3) {
            vector_istream stream(data);
            serialize_iarchive ar(stream);

            ar >> BOOST_SERIALIZATION_NVP(value0);
            ar >> BOOST_SERIALIZATION_NVP(value1);
            ar >> BOOST_SERIALIZATION_NVP(value2);
            ar >> BOOST_SERIALIZATION_NVP(value3);
        }
    };

    /*-----------------------------------------------------------------*/
    CommandData::CommandData() {
    }

    CommandData::CommandData(const std::vector<char> &data)
        : m_data(data) {
    }

    CommandData::~CommandData() {
    }

    data_container_t::size_type CommandData::GetSize() const
    {
        return m_data.size();
    }

    data_container_t & CommandData::GetImplData()
    {
        return m_data;
    }

    const data_container_t & CommandData::GetImplData() const
    {
        return m_data;
    }

    std::string CommandData::ToString() const
    {
        if (m_data.empty()) {
            return std::string("");
        }
        else {
            return std::string(&*m_data.begin(), m_data.size());
        }
    }

    void CommandData::Get_ChangedState(bool &isBreak) const {
        Serializer::ToValue(m_data, isBreak);
    }
    void CommandData::Set_ChangedState(bool isBreak) {
        m_data = Serializer::ToData(isBreak);
    }

    void CommandData::Get_UpdateSource(std::string &key, int &line, int &updateCount, bool &isRefreshOnly) const {
        Serializer::ToValue(m_data, key, line, updateCount, isRefreshOnly);
    }
    void CommandData::Set_UpdateSource(const std::string &key, int line, int updateCount, bool isRefreshOnly) {
        m_data = Serializer::ToData(key, line, updateCount, isRefreshOnly);
    }

    void CommandData::Get_AddedSource(Source &source) const {
        Serializer::ToValue(m_data, source);
    }
    void CommandData::Set_AddedSource(const Source &source) {
        m_data = Serializer::ToData(source);
    }

    void CommandData::Get_SaveSource(std::string &key, string_array &sources) const {
        Serializer::ToValue(m_data, key, sources);
    }
    void CommandData::Set_SaveSource(const std::string &key, const string_array &sources) {
        m_data = Serializer::ToData(key, sources);
    }

    void CommandData::Get_SetUpdateCount(int &updateCount) const {
        Serializer::ToValue(m_data, updateCount);
    }
    void CommandData::Set_SetUpdateCount(int updateCount) {
        m_data = Serializer::ToData(updateCount);
    }

    void CommandData::Get_SetBreakpoint(Breakpoint &bp) const {
        Serializer::ToValue(m_data, bp);
    }
    void CommandData::Set_SetBreakpoint(const Breakpoint &bp) {
        m_data = Serializer::ToData(bp);
    }

    void CommandData::Get_RemoveBreakpoint(Breakpoint &bp) const {
        Serializer::ToValue(m_data, bp);
    }
    void CommandData::Set_RemoveBreakpoint(const Breakpoint &bp) {
        m_data = Serializer::ToData(bp);
    }

    void CommandData::Get_ChangedBreakpointList(BreakpointList &bps) const {
        Serializer::ToValue(m_data, bps);
    }
    void CommandData::Set_ChangedBreakpointList(const BreakpointList &bps) {
        m_data = Serializer::ToData(bps);
    }

    //void CommandData::Get_SetEncoding(lldebug_Encoding &encoding) const {
    //    Serializer::ToValue(m_data, encoding);
    //}
    //void CommandData::Set_SetEncoding(lldebug_Encoding encoding) {
    //    m_data = Serializer::ToData(encoding);
    //}

    void CommandData::Get_OutputLog(LogData &logData) const {
        Serializer::ToValue(m_data, logData);
    }
    void CommandData::Set_OutputLog(const LogData &logData) {
        m_data = Serializer::ToData(logData);
    }

    void CommandData::Get_EvalsToVarList(string_array &evals, LuaStackFrame &stackFrame) const {
        Serializer::ToValue(m_data, evals, stackFrame);
    }
    void CommandData::Set_EvalsToVarList(const string_array &evals, const LuaStackFrame &stackFrame) {
        m_data = Serializer::ToData(evals, stackFrame);
    }

    void CommandData::Get_EvalToMultiVar(std::string &eval, LuaStackFrame &stackFrame) const {
        Serializer::ToValue(m_data, eval, stackFrame);
    }
    void CommandData::Set_EvalToMultiVar(const std::string &eval, const LuaStackFrame &stackFrame) {
        m_data = Serializer::ToData(eval, stackFrame);
    }

    void CommandData::Get_EvalToVar(std::string &eval, LuaStackFrame &stackFrame) const {
        Serializer::ToValue(m_data, eval, stackFrame);
    }
    void CommandData::Set_EvalToVar(const std::string &eval, const LuaStackFrame &stackFrame) {
        m_data = Serializer::ToData(eval, stackFrame);
    }

    void CommandData::Get_RequestFieldVarList(LuaVar &var) const {
        Serializer::ToValue(m_data, var);
    }
    void CommandData::Set_RequestFieldVarList(const LuaVar &var) {
        m_data = Serializer::ToData(var);
    }

    void CommandData::Get_RequestLocalVarList(LuaStackFrame &stackFrame, bool &checkLocal, bool &checkUpvalue, bool &checkEnviron) const {
        Serializer::ToValue(m_data, stackFrame, checkLocal, checkUpvalue, checkEnviron);
    }

    void CommandData::Set_RequestLocalVarList(const LuaStackFrame &stackFrame, bool checkLocal, bool checkUpvalue, bool checkEnviron) {
        m_data = Serializer::ToData(stackFrame, checkLocal, checkUpvalue, checkEnviron);
    }

    void CommandData::Get_RequestSource(std::string &key) {
        Serializer::ToValue(m_data, key);
    }
    void CommandData::Set_RequestSource(const std::string &key) {
        m_data = Serializer::ToData(key);
    }

    void CommandData::Get_ValueString(std::string &str) const {
        Serializer::ToValue(m_data, str);
    }
    void CommandData::Set_ValueString(const std::string &str) {
        m_data = Serializer::ToData(str);
    }

    void CommandData::Get_ValueSource(Source &source) const {
        Serializer::ToValue(m_data, source);
    }
    void CommandData::Set_ValueSource(const Source &source) {
        m_data = Serializer::ToData(source);
    }

    void CommandData::Get_ValueVarList(LuaVarList &vars) const {
        Serializer::ToValue(m_data, vars);
    }
    void CommandData::Set_ValueVarList(const LuaVarList &vars) {
        m_data = Serializer::ToData(vars);
    }

    void CommandData::Get_ValueVar(LuaVar &var) const {
        Serializer::ToValue(m_data, var);
    }
    void CommandData::Set_ValueVar(const LuaVar &var) {
        m_data = Serializer::ToData(var);
    }

    void CommandData::Get_ValueBacktraceList(LuaBacktraceList &backtraces) const {
        Serializer::ToValue(m_data, backtraces);
    }
    void CommandData::Set_ValueBacktraceList(const LuaBacktraceList &backtraces) {
        m_data = Serializer::ToData(backtraces);
    }

    /*-----------------------------------------------------------------*/
    LuaCommand::LuaCommand(void)
    {
    }

    LuaCommand::LuaCommand(const CommandHeader &header, const CommandData &data) :
    m_header(header), m_data(data)
    {

    }

    LuaCommand::~LuaCommand(void)
    {
    }

	struct RemoteCommandTypeName_t {
		RemoteCommandType type;
		std::string       name;
	};
	static RemoteCommandTypeName_t RemoteCommandTypeName[] =
	{
		{ REMOTECOMMANDTYPE_START_CONNECTION,       "REMOTECOMMANDTYPE_START_CONNECTION" },
		{ REMOTECOMMANDTYPE_END_CONNECTION,         "REMOTECOMMANDTYPE_END_CONNECTION" },

		{ REMOTECOMMANDTYPE_CHANGED_STATE,          "REMOTECOMMANDTYPE_CHANGED_STATE" },
		{ REMOTECOMMANDTYPE_UPDATE_SOURCE,          "REMOTECOMMANDTYPE_UPDATE_SOURCE" },
		{ REMOTECOMMANDTYPE_FORCE_UPDATESOURCE,     "REMOTECOMMANDTYPE_FORCE_UPDATESOURCE" },
		{ REMOTECOMMANDTYPE_ADDED_SOURCE,           "REMOTECOMMANDTYPE_ADDED_SOURCE" },
		{ REMOTECOMMANDTYPE_SAVE_SOURCE,            "REMOTECOMMANDTYPE_SAVE_SOURCE" },
		{ REMOTECOMMANDTYPE_SET_UPDATECOUNT,        "REMOTECOMMANDTYPE_SET_UPDATECOUNT" },

		{ REMOTECOMMANDTYPE_SET_BREAKPOINT,         "REMOTECOMMANDTYPE_SET_BREAKPOINT" },
		{ REMOTECOMMANDTYPE_REMOVE_BREAKPOINT,      "REMOTECOMMANDTYPE_REMOVE_BREAKPOINT" },
		{ REMOTECOMMANDTYPE_CHANGED_BREAKPOINTLIST, "REMOTECOMMANDTYPE_CHANGED_BREAKPOINTLIST" },

		{ REMOTECOMMANDTYPE_START,                  "REMOTECOMMANDTYPE_START" },
		{ REMOTECOMMANDTYPE_END,                    "REMOTECOMMANDTYPE_END" },
		{ REMOTECOMMANDTYPE_STEPINTO,               "REMOTECOMMANDTYPE_STEPINTO" },
		{ REMOTECOMMANDTYPE_STEPOVER,               "REMOTECOMMANDTYPE_STEPOVER" },
		{ REMOTECOMMANDTYPE_STEPRETURN,             "REMOTECOMMANDTYPE_STEPRETURN" },
		{ REMOTECOMMANDTYPE_BREAK,                  "REMOTECOMMANDTYPE_BREAK" },
		{ REMOTECOMMANDTYPE_RESUME,                 "REMOTECOMMANDTYPE_RESUME" },

		{ REMOTECOMMANDTYPE_SET_ENCODING,           "REMOTECOMMANDTYPE_SET_ENCODING" },
		{ REMOTECOMMANDTYPE_OUTPUT_LOG,             "REMOTECOMMANDTYPE_OUTPUT_LOG" },

		{ REMOTECOMMANDTYPE_EVALS_TO_VARLIST,       "REMOTECOMMANDTYPE_EVALS_TO_VARLIST" },
		{ REMOTECOMMANDTYPE_EVAL_TO_MULTIVAR,       "REMOTECOMMANDTYPE_EVAL_TO_MULTIVAR" },
		{ REMOTECOMMANDTYPE_EVAL_TO_VAR,            "REMOTECOMMANDTYPE_EVAL_TO_VAR" },

		{ REMOTECOMMANDTYPE_REQUEST_FIELDSVARLIST,  "REMOTECOMMANDTYPE_REQUEST_FIELDSVARLIST" },
		{ REMOTECOMMANDTYPE_REQUEST_LOCALVARLIST,   "REMOTECOMMANDTYPE_REQUEST_LOCALVARLIST" },
		{ REMOTECOMMANDTYPE_REQUEST_GLOBALVARLIST,  "REMOTECOMMANDTYPE_REQUEST_GLOBALVARLIST" },
		{ REMOTECOMMANDTYPE_REQUEST_REGISTRYVARLIST,"REMOTECOMMANDTYPE_REQUEST_REGISTRYVARLIST" },
		{ REMOTECOMMANDTYPE_REQUEST_STACKLIST,      "REMOTECOMMANDTYPE_REQUEST_STACKLIST" },
		{ REMOTECOMMANDTYPE_REQUEST_SOURCE,         "REMOTECOMMANDTYPE_REQUEST_SOURCE" },
		{ REMOTECOMMANDTYPE_REQUEST_BACKTRACELIST,  "REMOTECOMMANDTYPE_REQUEST_BACKTRACELIST" },

		{ REMOTECOMMANDTYPE_SUCCESSED,              "REMOTECOMMANDTYPE_SUCCESSED" },
		{ REMOTECOMMANDTYPE_FAILED,                 "REMOTECOMMANDTYPE_FAILED" },
		{ REMOTECOMMANDTYPE_VALUE_STRING,           "REMOTECOMMANDTYPE_VALUE_STRING" },
		{ REMOTECOMMANDTYPE_VALUE_SOURCE,           "REMOTECOMMANDTYPE_VALUE_SOURCE" },
		{ REMOTECOMMANDTYPE_VALUE_BREAKPOINTLIST,   "REMOTECOMMANDTYPE_VALUE_BREAKPOINTLIST" },
		{ REMOTECOMMANDTYPE_VALUE_VARLIST,          "REMOTECOMMANDTYPE_VALUE_VARLIST" },
		{ REMOTECOMMANDTYPE_VALUE_VAR,              "REMOTECOMMANDTYPE_VALUE_VAR" },
		{ REMOTECOMMANDTYPE_VALUE_BACKTRACELIST,    "REMOTECOMMANDTYPE_VALUE_BACKTRACELIST" },
	};

    std::string LuaCommand::GetTypeName() const
    {
        size_t cnt = sizeof(RemoteCommandTypeName) / sizeof(RemoteCommandTypeName_t);
        RemoteCommandTypeName_t * iter = find_if(
            RemoteCommandTypeName,RemoteCommandTypeName+cnt,
            boost::bind(std::equal_to<RemoteCommandType>(),
            boost::bind(&RemoteCommandTypeName_t::type,_1),m_header.u.type));
        return iter != RemoteCommandTypeName+cnt ? iter->name : "";
    }
} // namespace LuaDebug
